using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
using PddWebAuto.Helper;
using PddWebAuto.Model;
using PddWebAuto.PddModel;
using PddWebAuto.PddSearch.DbModel;
using PuppeteerSharp;

namespace PddWebAuto.PddSearch
{
    /// <summary>
    /// 关键字搜索服务
    /// </summary>
    public class KeyWordSearchService
    {
        /// <summary>
        /// 搜索浏览器
        /// </summary>
        public ChromeDrive SearchDriver { get; set; }

        /// <summary>
        /// 详情浏览器
        /// </summary>
        public List<ChromeDrive> DetailDrives { get; set; }

        /// <summary>
        /// 搜索设置
        /// </summary>
        public SearchSettingModel Setting { get; set; }

        /// <summary>
        /// 日志事件
        /// </summary>
        private Action<string> LogEvent { get; set; }

        /// <summary>
        /// 商品队列
        /// </summary>
        private ConcurrentQueue<ShopItem> ShopItemsQueue = new ConcurrentQueue<ShopItem>();

        /// <summary>
        /// 已成功处理的数量
        /// </summary>
        public int MatchGoodsNum = 0;

        /// <summary>
        /// Excel的数据源
        /// </summary>
        private DataTable excelDataSource;

        /// <summary>
        /// 存储路径
        /// </summary>
        private string excelSavePath;

        /// <summary>
        /// 共享的随机数
        /// </summary>
        private Random RandomShared = new Random(DateTime.Now.Millisecond);

        /// <summary>
        /// 滚动完成
        /// </summary>
        private bool WellEnd;

        public KeyWordSearchService()
        {
        }

        public KeyWordSearchService(ChromeDrive searchDriver, List<ChromeDrive> detailDrives,
            SearchSettingModel setting, Action<string> logEvent) : this()
        {
            this.SearchDriver = searchDriver;
            this.DetailDrives = detailDrives;
            this.Setting = setting;
            this.LogEvent = logEvent;
        }

        /// <summary>
        /// 运行搜索
        /// </summary>
        public async Task RunAsync()
        {
            try
            {
                //初始化
                await this.InitAsync();

                //初始数据
                this.SearchInitPageRawDataAsync();

                //开启滚动
                Task.Run(RunSearchResultWellAsync);

                //处理队列
                await HandleQueueAsync();
            }
            catch (Exception)
            {
                // ignored
            }
            finally
            {
                await UnloadAsync();
            }
        }

        /// <summary>
        ///  卸载事件
        /// </summary>
        private async Task UnloadAsync()
        {
            this.SearchDriver._currentPage.Response -= CurrentPageOnResponse;
            this.SearchDriver._currentPage.Request -= CurrentPageOnRequest;
        }

        private string _searchKey = string.Empty;


        public void Test()
        {
            excelSavePath = Path.Combine(AppClass.PddSearchDataPath, DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx");

            excelDataSource = new DataTable();
            excelDataSource.Columns.Add("商品id");
            excelDataSource.Columns.Add("商品名称");
            excelDataSource.Columns.Add("商品价格");
            excelDataSource.Columns.Add("店铺名称");
            excelDataSource.Columns.Add("商品链接");
            excelDataSource.Columns.Add("商品标签");
            excelDataSource.Columns.Add("商品评价");
            excelDataSource.Columns.Add("销量");
            excelDataSource.Columns.Add("拼单人数");
            excelDataSource.Columns.Add("sku");
            
            
            SaveToFile();
        }


        /// <summary>
        /// 初始化搜索
        /// </summary>
        private async Task InitAsync()
        {
            SearchDriver.Log("正在初始化采集参数");

            this.SearchDriver._currentPage.Response -= CurrentPageOnResponse;
            this.SearchDriver._currentPage.Response += CurrentPageOnResponse;
            this.SearchDriver._currentPage.Request -= CurrentPageOnRequest;
            this.SearchDriver._currentPage.Request += CurrentPageOnRequest;

            excelSavePath = Path.Combine(AppClass.PddSearchDataPath, DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx");

            excelDataSource = new DataTable();
            excelDataSource.Columns.Add("商品id");
            excelDataSource.Columns.Add("商品名称");
            excelDataSource.Columns.Add("商品价格");
            excelDataSource.Columns.Add("店铺名称");
            excelDataSource.Columns.Add("商品链接");
            excelDataSource.Columns.Add("商品标签");
            excelDataSource.Columns.Add("商品评价");
            excelDataSource.Columns.Add("销量");
            excelDataSource.Columns.Add("拼单人数");
            excelDataSource.Columns.Add("sku");
            MatchGoodsNum = 0;
            Setting.SetStatusLabel(3, MatchGoodsNum.ToString());
            Setting.SetStatusLabel(1, "1");
            SearchDriver.Log("初始化采集参数成功");
        }

        /// <summary>
        /// 处理队列数据
        /// </summary>
        private async Task HandleQueueAsync()
        {
            var index = 0;
            var mallNameBlack = File.Exists(Path.Combine(AppClass.PddSearchPath, "店铺黑名单.txt"))
                ? File.ReadAllLines(Path.Combine(AppClass.PddSearchPath, "店铺黑名单.txt"))
                : Array.Empty<string>();
            while (Program._Run_ == 1)
            {
                try
                {
                    if (Setting.BfSearch)
                    {
                        if (this.DetailDrives.Any(x => !x.Busy))
                        {
                            foreach (var driver in this.DetailDrives)
                            {
                                if (Program._Run_ == 0) break;
                                if (driver.Busy || !driver.Valid()) continue;
                                driver.Busy = true;
                                HandleDetailAsync(driver, mallNameBlack);
                            }
                        }

                        await TaskHelper.Delay(1000);
                    }
                    else
                    {
                        if (this.DetailDrives.Any(x => x.Busy))
                        {
                            await TaskHelper.Delay(1000);
                            continue;
                        }

                        //得到负责本轮采集的浏览器对象
                        var driver = this.DetailDrives[index];
                        index++;
                        if (index >= this.DetailDrives.Count) index = 0;
                        var tryCount = 0;
                        while (!driver.Valid())
                        {
                            if (Program._Run_ == 0) break;
                            tryCount++;
                            driver = this.DetailDrives[index];
                            index++;
                            if (index >= this.DetailDrives.Count) index = 0;
                            if (tryCount > 10)
                            {
                                SearchDriver.Log("采集商品详情的浏览器均不存在,采集结束！");
                                Program._Run_ = 0;
                                return;
                            }
                        }

                        try
                        {
                            driver.Busy = true;
                            HandleDetailAsync(driver, mallNameBlack);
                        }
                        catch (Exception e)
                        {
                            // ignored
                        }
                    }
                }
                catch (Exception e)
                {
                    // ignored
                }
            }
        }

        /// <summary>
        /// 处理详情
        /// </summary>
        /// <param name="driver"></param>
        /// <param name="mallNameBlack"></param>
        /// <returns></returns>
        private async Task<bool> HandleDetailAsync(ChromeDrive driver, string[] mallNameBlack)
        {
            try
            {
                if (this.ShopItemsQueue.Count == 0 || !this.ShopItemsQueue.TryDequeue(out var shopItem) ||
                    shopItem == null)
                {
                    if (WellEnd)
                    {
                        SearchDriver.Log("全部列表已执行完毕,抓取完成!");
                        Program._Run_ = 0;
                        return false;
                    }

                    await TaskHelper.Delay(1000);
                    return false;
                }

                if (!long.TryParse(shopItem.MallId, out var mallId))
                {
                    SearchDriver.Log($"商品:{shopItem.GoodsID} 没有获取到店铺ID");
                    return false;
                }

                lock (DbHelper.lockObj)
                {
                    if (DbHelper.MallIsExist(mallId))
                    {
                        SearchDriver.Log($"商品:{shopItem.GoodsID} 已经处理过该店铺");
                        return false;
                    }
                }

                driver.Log($"商品:{shopItem.GoodsID} 正在抓取");
                var url = $"http://mobile.yangkeduo.com/goods.html?goods_id={shopItem.GoodsID}";
                var tryCount = 0;
                retry:
                tryCount++;
                if (!await driver._currentPage.MyGoToAsync(driver.ChromeProcess, url))
                {
                    if (Program._Run_ == 0) return false;
                    if (tryCount >= 3)
                    {
                        driver.Log($"商品:{shopItem.GoodsID} 页面加载失败");
                        return false;
                    }

                    goto retry;
                }

                driver.Log($"商品:{shopItem.GoodsID} 检测是否有验证码");

                if (await driver.IsVerification())
                {
                    await TaskHelper.Delay(500);
                    await driver.VerificationHanlder();
                    if (Program._Run_ == 0) return false;
                }

                if (await driver.IsVerification())
                {
                    driver.Log($"商品:{shopItem.GoodsID} 验证码处理失败");

                    return false;
                }

                if (Program._Run_ == 0) return false;

                driver.Log($"商品:{shopItem.GoodsID} 解析数据");

                //读取页面的html
                var pageHtml = await driver._currentPage.GetContentAsync();
                if (string.IsNullOrWhiteSpace(pageHtml))
                {
                    driver.Log($"商品:{shopItem.GoodsID} 详情页面数据为空~");

                    return false;
                }

                if (pageHtml.Contains("原商品已售罄，为你推荐相似商品"))
                {
                    driver.Log("发现,原商品已售罄，为你推荐相似商品,可能账户被限制!");
                    driver._DetailError++;
                    if (driver._DetailError >= 3)
                    {
                        try
                        {
                            driver.Log("多次发现,原商品已售罄，为你推荐相似商品,已排除该浏览器参与任务!");
                            driver.ChromeProcess?.Close();
                        }
                        catch (Exception)
                        {
                            // ignored
                        }
                    }

                    return false;
                }

                var good = this.GetGoods(pageHtml);
                if (good == null)
                    return false;
                good.Goods_name = shopItem.GoodsName;
                good.Goods_id = shopItem.GoodsID;
                good.Link = url;
                good.Mall_id = mallId;
                good.Price_info = shopItem.PriceInfo;

                if (Program._Run_ == 0) return false;

                if (Setting.FilterTag.Any(a => good.Goods_tags.Contains(a)))
                {
                    SearchDriver.Log($"商品:{shopItem.GoodsID} 不符合过滤条件");
                    return false;
                }

                if (mallNameBlack.Any(a => a.Contains(good.Mall_name)))
                {
                    SearchDriver.Log($"商品:{shopItem.GoodsID} 匹配店铺黑名单");
                    return false;
                }

                lock (DbHelper.lockObj)
                {
                    if (Program._Run_ == 0) return false;

                    if (DbHelper.MallIsExist(mallId))
                    {
                        SearchDriver.Log($"商品:{shopItem.GoodsID} 已经处理过该店铺");
                        return false;
                    }

                    //mallNameBlack
                    if (DbHelper.MallAdd(good.Mall_id, good.Mall_name))
                    {
                        SearchDriver.Log(good.Mall_name + "/" + good.Goods_tags);

                        //32768
                        if (good.Skus.Length < 25000)
                        {
                            MatchGoodsNum++;
                            Setting.SetStatusLabel(3, MatchGoodsNum.ToString());
                            excelDataSource.Rows.Add(good.Goods_id, good.Goods_name, good.Price_info,
                                good.Mall_name, good.Link, good.Goods_tags, good.Evaluation, good.Sales,
                                good.MergeNum, good.Skus);
                        }

                        if (MatchGoodsNum >= Setting.MaxSearchNum)
                        {
                            SearchDriver.Log($"搜索商品数量已达标~ 结束任务");
                            Program._Run_ = 0;
                        }

                        Task.Run(SaveToFile);
                    }
                    else
                    {
                        SearchDriver.Log($"商品:{shopItem.GoodsID} 所在店铺已处理过,放弃保存");
                    }
                }

                driver.Log($"商品:{shopItem.GoodsID} 完成抓取");
                return true;
            }
            catch (Exception e)
            {
                // ignored
                return false;
            }
            finally
            {
                driver.Busy = false;
            }
        }

        /// <summary>
        /// 获取商品详情
        /// </summary>
        /// <param name="html"></param>
        /// <returns></returns>
        private GoodsInfoModel GetGoods(string html)
        {
            try
            {
                var rawData = Regex.Match(html, "window.rawData=([\\s\\S]*?\\});").Groups[1].Value;
                if (rawData.Contains("{\"needLogin\":true}"))
                {
                    return default;
                }

                var goods = new GoodsInfoModel();
                var Json = JObject.Parse(rawData);
                goods.Evaluation = (int)Json.SelectToken("store.initDataObj.oakData.review.reviewNum");
                var servicePromise = Json.SelectToken("store.initDataObj.oakData.servicePromise") as JArray;
                if (servicePromise != null)
                {
                    foreach (var item in servicePromise)
                    {
                        goods.Goods_tags += (string)item["type"] + "/";
                    }

                    goods.Goods_tags = goods.Goods_tags.TrimEnd('/');
                }

                goods.Mall_name = (string)Json.SelectToken("store.initDataObj.mall.mallName");
                goods.Sales = (string)Json.SelectToken("store.initDataObj.goods.sideSalesTip");
                var combineGroupDesc =
                    (string)Json.SelectToken(
                        "store.initDataObj.goods.neighborGroup.neighbor_data.combine_group.combine_group_desc") ?? "";
                var Match = Regex.Match(combineGroupDesc, "(\\d+)");
                goods.MergeNum = 0;
                if (Match.Success)
                {
                    goods.MergeNum = int.Parse(Match.Groups[1].Value);
                }

                var skus = Json.SelectToken("store.initDataObj.goods.skus") as JArray;
                var GoodsInfos = new List<GoodsSkuModel>();
                if (skus != null)
                {
                    foreach (var item in skus)
                    {
                        var skuId = (string)item["skuId"];
                        var quantity = (int)item["quantity"];
                        var Price = (string)item.SelectToken("priceDisplay.price") ??
                                    (string)item.SelectToken("groupPrice");
                        var specs = item.SelectToken("specs") as JArray;
                        var specsList = new List<string>();
                        var hot = ((JObject)item).ContainsKey("hotSale")
                            ? item.SelectToken("hotSale")?.Value<string>()
                            : string.Empty;
                        hot = hot?.Trim();
                        if (specs != null)
                        {
                            foreach (var spec in specs)
                            {
                                specsList.Add((hot == "1" ? "(火火火)" : "") + (string)spec["spec_value"]);
                            }
                        }

                        GoodsInfos.Add(new GoodsSkuModel
                            { skuId = skuId, price = Price, quantity = quantity, skus = specsList });
                    }
                }

                goods.Skus = JsonConvert.SerializeObject(GoodsInfos);
                return goods;
            }
            catch (Exception)
            {
                // ignored
            }

            return default;
        }


        /// <summary>
        /// 搜索初始页的商品数据
        /// </summary>
        private async void SearchInitPageRawDataAsync()
        {
            try
            {
                var pageHtml = await this.SearchDriver._currentPage.GetContentAsync();
                const string rawDataFlag = "window.rawData=";
                var rawDataStaIndex = pageHtml.IndexOf("window.rawData=", StringComparison.Ordinal);
                var rawDataEndIndex =
                    pageHtml.IndexOf(";document.dispatchEvent", rawDataStaIndex, StringComparison.Ordinal);
                var rawData = pageHtml.Substring(rawDataStaIndex + rawDataFlag.Length,
                    rawDataEndIndex - rawDataStaIndex - rawDataFlag.Length);
                var rawDataObject = JObject.Parse(rawData);
                var array = (JArray)rawDataObject.SelectToken("stores.store.data.ssrListData.list");
                this._searchKey = rawDataObject.SelectToken("stores.store.data.ssrListData.searchKey")?.Value<string>();
                if (!string.IsNullOrWhiteSpace(_searchKey))
                {
                    excelSavePath = Path.Combine(AppClass.PddSearchDataPath,
                        _searchKey + DateTime.Now.ToString("yyyyMMddHHmmss") + ".xlsx");
                }
                SearchDriver.Log($"搜索关键字为:{_searchKey}");
                var shopItems = array?.ToObject<List<ShopItem>>();
                if (shopItems != null)
                {
                    foreach (var item in array)
                    {
                        var goodsId = item.SelectToken("goodsID")?.Value<string>();
                        if (string.IsNullOrWhiteSpace(goodsId)) continue;
                        var shopItem = shopItems.FirstOrDefault(x => x.GoodsID == goodsId);
                        if (shopItem == null) continue;
                        var mallId = item.SelectToken("mallEntrance.mall_id")?.Value<string>();
                        shopItem.MallId = mallId;
                    }

                    foreach (var item in shopItems)
                    {
                        ShopItemsQueue.Enqueue(item);
                    }
                }

                SearchDriver.Log($">>>搜索页初始数据量:{ShopItemsQueue.Count}");
            }
            catch (Exception)
            {
                // ignored
            }
        }

        /// <summary>
        /// 滚动搜索页
        /// </summary>
        private async Task RunSearchResultWellAsync()
        {
            const string selector = "#main";
            while (SearchDriver != null && SearchDriver.Valid() && Program._Run_ == 1)
            {
                try
                {
                    var WheelX = RandomShared.Next(50, 150);
                    var WheelY = RandomShared.Next(Setting.WheelStartL, Setting.WheelEndL);
                    await SearchDriver._currentPage.MySelectorMove(selector, WheelX, WheelY);
                    await ListWheelAsync(selector, WheelX, WheelY, WheelY, false);
                    await TaskHelper.Delay(RandomShared.Next(Setting.WheelStartSleep, Setting.WheelEndSleep));
                    if (!SearchDriver.Valid())
                    {
                        break;
                    }

                    if (!await SearchDriver.IsVerification())
                    {
                        var html = await SearchDriver._currentPage.GetContentAsync();
                        if (html.Contains("页面正在加载中，请稍后") && html.Contains("重新加载"))
                        {
                            await SearchDriver._currentPage.MyGoToAsync(SearchDriver.ChromeProcess,
                                SearchDriver._currentPage.Url);
                        }

                        if (html.Contains("已显示完全部搜索结果"))
                        {
                            SearchDriver.Log("该关键字已全部滑动完成!");
                            WellEnd = true;
                            break;
                        }

                        continue;
                    }

                    SearchDriver.Log("搜索页处理验证码");
                    while (!await SearchDriver.VerificationHanlder())
                    {
                        if (Program._Run_ == 0) break;
                        await TaskHelper.Delay(1000);
                    }
                }
                catch (Exception)
                {
                    // ignored
                }
            }

            Program._Run_ = 0;
        }


        /// <summary>
        /// 滚动搜索列表
        /// </summary>
        /// <param name="X"></param>
        /// <param name="Y"></param>
        /// <param name="deltaY"></param>
        /// <param name="Move"></param>
        private async Task ListWheelAsync(string selector, int X, int Y, int deltaY, bool Move)
        {
            try
            {
                await SearchDriver._currentPage.MySelectorWheel(selector, X, Y, deltaY, Move);
            }
            catch (Exception)
            {
                // ignored
            }
        }


        private void CurrentPageOnRequest(object Sender, RequestEventArgs E)
        {
            E.Request.ContinueAsync();
        }

        /// <summary>
        /// 拦截的响应数据
        /// </summary>
        /// <param name="Sender"></param>
        /// <param name="E"></param>
        private async void CurrentPageOnResponse(object Sender, ResponseCreatedEventArgs E)
        {
            try
            {
                if (!E.Response.Url.Contains("/proxy/api/search")) return;
                //得到文本信息
                var response = await E.Response.TextAsync();
                var items = (JArray)JObject.Parse(response).SelectToken("items");
                if (items == null || items.Count == 0) return;
                foreach (var item in items)
                {
                    var data = item.SelectToken("item_data.goods_model");
                    if (data == null) continue;
                    var name = data.SelectToken("goods_name")?.Value<string>();
                    var link = data.SelectToken("link_url")?.Value<string>();
                    var id = data.SelectToken("goods_id")?.Value<string>();
                    var mallId = data.SelectToken("mall_id")?.Value<string>();
                    var price = data.SelectToken("price_info")?.Value<string>();
                    this.ShopItemsQueue.Enqueue(new ShopItem()
                        { GoodsName = name, LinkURL = link, GoodsID = id, MallId = mallId, PriceInfo = price });
                }

                SearchDriver.Log($"新的搜索结果已抓取,目前排队:{this.ShopItemsQueue.Count}条~");
            }
            catch (Exception)
            {
                // ignored
            }
        }


        /// <summary>
        /// 保存到文件
        /// </summary>
        private void SaveToFile()
        {
            lock (excelSavePath)
            {
                try
                {
                    var workbook = new XSSFWorkbook();
                    var sheet = workbook.CreateSheet("商品列表采集结果");

                    //header
                    {
                        var row = sheet.CreateRow(0);
                        for (var i = 0; i < excelDataSource.Columns.Count; i++)
                        {
                            var cell = row.CreateCell(i);
                            cell.SetCellValue(excelDataSource.Columns[i].ColumnName);
                        }
                    }
                    //data_rows
                    {
                        for (var i = 0; i < excelDataSource.Rows.Count; i++)
                        {
                            var itemRow = excelDataSource.Rows[i];
                            var row = sheet.CreateRow(i + 1);
                            for (var j = 0; j < excelDataSource.Columns.Count; j++)
                            {
                                var cell = row.CreateCell(j);
                                var value = itemRow[j]?.ToString() ?? string.Empty;
                                cell.SetCellValue(value);
                            }
                        }
                    }
                    ExcelHelper.AutoColumnWidth(sheet, excelDataSource.Columns.Count);
                    if (File.Exists(excelSavePath))
                    {
                        File.Delete(excelSavePath);
                    }

                    using (var sw = File.OpenWrite(excelSavePath))
                    {
                        workbook.Write(sw);
                    }

                    workbook.Dispose();
                }
                catch (Exception)
                {
                    // ignored
                }
            }
        }
    }

    /// <summary>
    /// 商品项
    /// </summary>
    public class ShopItem
    {
        /// <summary>
        /// 商品名称
        /// </summary>
        public string GoodsName { get; set; }

        /// <summary>
        /// ID
        /// </summary>
        public string GoodsID { get; set; }

        /// <summary>
        /// 商品价格
        /// </summary>
        public string PriceInfo { get; set; }

        /// <summary>
        /// 店铺ID
        /// </summary>
        public string MallId { get; set; }

        /// <summary>
        /// 链接
        /// </summary>
        public string LinkURL { get; set; }
    }
}